package com.tools.commons.base;

import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.io.Serializable;
import java.util.List;
import java.util.Optional;

public class BaseRepositoryImpl<T extends BaseEntity, ID extends Serializable>
        extends SimpleJpaRepository<T, ID>
        implements BaseRepository<T, ID> {

    private final EntityManager entityManager;
    private final JpaEntityInformation<T, ?> entityInformation;

    public BaseRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
        super(entityInformation, entityManager);
        this.entityManager = entityManager;
        this.entityInformation = entityInformation;
    }

    @Override
    public void deleteByBusinessId(String businessId) {
        ByBusinessIdSpecification<T> specification = new ByBusinessIdSpecification<>();
        TypedQuery<T> query = super.getQuery(specification, Sort.unsorted());
        List<T> resultList = query.setParameter(specification.parameter, businessId).getResultList();
        if (resultList != null && resultList.size() > 0) {
            T t = resultList.get(0);
            t.setDeleted(true);
            entityManager.persist(t);
        }
    }

    @Override
    public Optional<T> findByBusinessId(String businessId) {
        ByBusinessIdSpecification<T> spec = new ByBusinessIdSpecification<>();
        TypedQuery<T> query = super.getQuery(spec, Sort.unsorted());
        List<T> resultList = query.setParameter(spec.parameter, businessId).getResultList();
        if (resultList != null && resultList.size() > 0) {
            return Optional.of(resultList.get(0));
        }
        return Optional.empty();
    }


    private static final class ByBusinessIdSpecification<T> implements Specification<T> {
        private static final long serialVersionUID = 1L;

        private ParameterExpression<String> parameter;


        public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            Path<?> path = root.get("businessId");
            this.parameter = cb.parameter(String.class);
            return cb.equal(root, this.parameter);
        }
    }
}
